package com.csx.base.concurrency.aqs.reentrantlock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p> what does this class do ? </p>
 *  本案例测试ReentrantLock加锁解锁流程
 *    ① ReentrantLock正常加锁的流程
 *    ② ReentrantLock无法获取锁后进入同步队列的流程
 *    ③ ReentrantLock解锁流程
 *   tips: 搜索 【断点】 快速找到断点处
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/12/2 周一 8:47
 */
public class ReentrantLockLockUnlockTest {

    private int total = 0;

    private final ReentrantLock lock = new ReentrantLock();


    // 1. 测试正常加锁，且能立即获取到锁①; 测试正常解锁③
    public void testNormalLock() {

        Thread thread1 = new Thread(new AddTask());
        Thread thread2 = new Thread(new AddTask());
        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("执行完毕,执行结果为: " + total + " 是否为预计结果: " + (2000 == total));
    }

    // 2. 测试加锁但是由于其它线程持有锁而无法正常获取,只能进入到阻塞队列中等待持锁线程释放锁②
    public void testLockBlockQueue() {
        Thread thread1 = new Thread(new LongTimeTask());
        Thread thread2 = new Thread(new OneSecondTask());
        thread1.start();
        try {
            // 等待线程1成功启动获取到锁
            TimeUnit.SECONDS.sleep(3);
            thread2.start();
            // 等待两个线程完成任务
            thread1.join();
            thread2.join();
            System.out.println("任务执行完毕");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    class AddTask implements Runnable {

        public void add() {
            // 断点: 测试正常加锁并能成功获取到锁
            lock.lock();
            try {
                // 模拟任务执行耗时
                TimeUnit.MILLISECONDS.sleep(1);
                total++;
            }catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                // 断点: 测试正常解锁
                lock.unlock();
            }
        }

        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                add();
            }
        }
    }

    class LongTimeTask implements Runnable {

        @Override
        public void run() {
            lock.lock();
            try {
                // 模拟长任务的执行时长,这会导致长时间持有锁,后续想要获取锁的线程只能进入阻塞队列等待释放锁
                TimeUnit.SECONDS.sleep(99999);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }

    class OneSecondTask implements Runnable {

        @Override
        public void run() {
            // 断点: 测试加锁但是无法获取到锁，进入到同步队列中阻塞等待
            lock.lock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally {
                lock.unlock();
            }
        }
    }

}
